<?php
/**
 * 
 * +------------------------------------------------------------+
 * @category EasyView 
 * +------------------------------------------------------------+
 * 框架内置模板引擎
 * +------------------------------------------------------------+
 *
 * @author anzai <236049524@qq.com>
 * @copyright http://www.judianyun.com 2013
 * @version 1.0
 *
 * Modified at : 2013-2-17 14:52:17
 *
 */
class EasyView extends View {
	
	///内置模板引擎类名
	private static $_viewClassName = 'EasyView';
	
	///存储每个循环变量当前索引、大小等参数
	private $_foreach;
	
	//对模板进行赋值
	public function assign($name,$value = ''){
		if(is_array($name)){
			$value = is_array($value) ? $value : array($value);
			foreach ($name as $key=>$vo){
				if (empty($value) && is_array($value)){
					$this->_vars[$vo] = $value[$key];
				}else{
					$this->_vars[$key] = $vo;
				}
			}
				
		}elseif($name)
			$this->_vars[$name] = $value;
	}
	
	//对字符串类型的模板代码进行编译
	public function compile($string){
		return $this->_compile($string, false);
	}
	
	public function fetchFromFile($compile_file){
		ob_start();
		if(!empty($this->_vars)){
			extract($this->_vars,EXTR_OVERWRITE);
		}
		if (is_file($compile_file)) include $compile_file;
		$out = ob_get_clean();
		
		return $out;
	}
	
	private function _get_var($name){
		if ($this->_vars){
			if (isset($this->_vars[$name])){
				return $this->_vars[$name];
			}elseif (substr($name, 0, 1) == '$'){
				$name = substr($name, 1);
				if (strpos($name, '.') === false){
					return isset($this->_vars[$name]) ? $this->_vars[$name] : '';
				}else{
					$arr = explode('.', $name);
					if (isset($this->_vars[$arr[0]])){
						$var = $this->_vars[$arr[0]];
						$length = count($arr);
						for($i = 1; $i < $length; $i++){
							if (isset($var[$arr[$i]])){
								$var = $var[$arr[$i]];
							}else{
								return '';
							}
						}
						
						return $var;
					}else{
						return '';
					}
				}
			}
		}else{
			return '';
		}
	}
	
	public function fetch($filename='', $cache_id = 0, $suffix = ''){
		if (strpos($filename, '$') !== false){
			$__tplarr = explode(':', str_replace(array('/', '\\'), ':', $filename));
			foreach ($__tplarr as $__k => $__tpl){
				if(substr($__tpl, 0, 1) == '$'){
					$__tplarr[$__k] = $this->_get_var($__tpl);
				}
			}
			
			$filename = implode(':', $__tplarr);
			unset($__tplarr);
		}
		
		$info = $this->_parseTpl($filename, $cache_id, $suffix);
		
		//是否创建静态缓存标识
		$create = false;
		
		if ($this->caching && $cache_id !== 0) {
			$static = $info['static'];
			if (file_exists($static) && ($this->lifetime <= 0 || time() - filemtime($static) > $this->lifetime)){
				ob_start();
				include $static;
				return ob_get_clean();
			}
			$create = true;
		}
		
		//模板路径
		$template = $info['template'];
		
		//模板编译文件路径
		$compile = $info['compile'] . '.php';
		unset($info);
		if (file_exists($template) && (!file_exists($compile) || filemtime($template)>filemtime($compile))){
			//重新编译文件
			$this->_compile($template, $compile);
		}
		
		ob_start();
		if($this->_vars){
			extract($this->_vars, EXTR_OVERWRITE);
		}
		include $compile;
		$out = ob_get_clean();
		if ($create) {
			//创建静态缓存
			Helper::write($static, $out);
		}
		
		return $out;
	}
	
	///解析foreach标签
	static public function parseForeach($var){
		$str = '<?php ';
		
		if(substr(trim($var[1]), 0 ,1) != '$'){
			static $idx = 0;
			$key = md5('tmpArr' . $idx);
			$varname = '$tmpArr' . $idx;
			$str .= $varname . '=' . $var[1] . ';';
			$var[1] = $varname;
			$idx ++;
		}else{
			$key = md5(substr($var[1], 1));
		}
		$str .= $var[1].'=is_array(' . $var[1] . ')?' . $var[1] . ':(empty(' . $var[1] . ')?array():array(' . $var[1] . '));';
		$str .= '$this->_foreach[\'' . $key . '\'][\'count\']=count(' . $var[1] . '); $this->_foreach[\'' . $key .'\'][\'index\']=0;';
		$str .= 'foreach('. $var[1] . ' as ' . $var[2] . '):';
		$str .= '$this->_foreach[\'' .  $key. '\'][\'index\']++; ?>';
		
		return $str;
	}
	
	///解析foreach奇、偶、总共个数标签
	static public function parseForeachVar($var,$fix=true){
		$key = md5($var[2]);
		$str = '$this->_foreach[\''.$key.'\'][\'index\']';
		switch (strtolower($var[3])){
			case 'first':
				$str = '('.$str.'==1)';
				break;
			case 'last':
				$str = '('.$str.'==$this->_foreach[\''.$key.'\'][\'count\'])';
				break;
			case 'count':
				$str = '$this->_foreach[\''.$key.'\'][\'count\']';
				break;
		}
		if ($var[4]){
			$var[4] = preg_replace_callback('/(.*?)\$foreach\.([^\}^\.]+?)\.(first|last|index|count)(.*?)/s',self::$_viewClassName.'::parseChildForeach',$var[4]);
		}
		if ($fix)
			return '{'.$var[1].$str.$var[4].'}';
		else
			return $var[1].$str.$var[4];
	}
	
	static public function parseChildForeach($var){
		return self::parseForeachVar($var,false);
	}
	
	//引入模板
	static public function parseInclude($var){
		$params = trim($var[3]);
		$php = '<?php ';
		if ($params) {
			$params = explode(' ', $params);
			foreach ($params as $vo){
				$vo = trim($vo);
				if ($vo){
					$vo = explode('=', $vo);
					$php .= '$this->assign(\''.$vo[0].'\','.($vo[1]=='\'\''?'':$vo[1]).');';
				}
			}
		}
		$cacheid = empty($var[1]) ? 0 : trim(str_replace(':', '', $var[1]));
		$cacheid = is_numeric($cacheid) ? $cacheid : "'{$cacheid}'";
		$php .= 'echo $this->fetch(\''.addslashes($var[2]).'\','.$cacheid.');';
		$php .= ' ?>';
		return $php;
	}
	
	///编译模板
	private function _compile($template, $compile){
		$content = is_file($template) || file_exists($template) ? file_get_contents($template) : $template;
		//内容为空，则不进行编译处理直接生成编译文件
		if (empty($content)){
			$compile !== false AND Helper::write($compile, $content);
			return ;
		}
		
		//将$G.var替换为$_GET['var']，$P.var替换为$_POST['var']
		$content = preg_replace('/\{(.*)\$G\.(\w+)(.*)\}/i','{\\1$_GET[\'\\2\']\\3}',$content);
		$content = preg_replace('/\{(.*)\$P\.(\w+)(.*)\}/i','{\\1$_POST[\'\\2\']\\3}',$content);
		
		//将_MODULE_替换为当前控制器名，_ACTION_替换为当前动作名
		$content = preg_replace('/\{(.*)(_MODULE_)(.*)\}/i','{\\1$GLOBALS[\'MODULE_NAME\']\\3}',$content);
		$content = preg_replace('/\{(.*)(_ACTION_)(.*)\}/i','{\\1$GLOBALS[\'ACTION_NAME\']\\3}',$content);
		
		//解析foreach语句
		$content = preg_replace_callback('/\{foreach\s+([^\}]+?)\s+as([^}]+?)\}/si',self::$_viewClassName.'::parseForeach',$content);
		$content = preg_replace_callback('/\{([^\{]*?)\$foreach\.([^\}^\.]+?)\.(first|last|index|count)([^\}]*?)\}/s',self::$_viewClassName.'::parseForeachVar',$content);
		$content = str_ireplace('{/foreach}','<?php endforeach; ?>',$content);
		
		//解析for语句
		$content = preg_replace('/\{for\s+([^\}]*?)\s*;\s*([^}]*?)\s*;\s*([^}]*?)\}/si','<?php for(\\1;\\2;\\3): ?>',$content);
		$content = str_ireplace('{/for}','<?php endfor; ?>',$content);
		
		//将{$var|default}替换成“$var ? $var : default”格式
		$content = preg_replace('/\{(\$[a-zA-Z_]+[^\|\s]*)\|([^\|\s]+?.*?)\}/','<?php echo \\1 ? \\1 : \\2; ?>',$content);
		$content = preg_replace('/\{(\$[a-zA-Z_]+.*?)\}/','<?php echo \\1; ?>',$content); //匹配替换{$var}格式
		$content = preg_replace('/\{@(\$[a-zA-Z_]+.*?)\}/','<?php \\1; ?>',$content); //匹配替换{@$var}格式，表示不输出变量
		
		//引入文件，若有使用缓存格式为{2:include template}
		$content = preg_replace_callback('/\{([\w1-9]+:)?include\s+(.+?)((\s+.*?)*)\}/im', self::$_viewClassName.'::parseInclude', $content);
		$content = preg_replace('/\{([A-Z_]+)\}/','<?php if(defined("\\1")) echo \\1; ?>',$content); //匹配替换全局变量
		
		//匹配替换if条件语句
		$content = preg_replace('/\{if\s+(.*?)\}/i','<?php if(\\1): ?>',$content);
		$content = preg_replace('/\{else\s*if\s+(.*?)\}/i','<?php elseif(\\1): ?>',$content);
		$content = str_ireplace('{else}','<?php else: ?>',$content);
		$content = str_ireplace('{/if}','<?php endif; ?>',$content);
		
		//在模板上调用已定义的函数，前面有@表示不输出结果
		preg_match_all('/\{@?([a-zA-Z_\:]+\w*).+?\}/s',$content,$matches);
		if (!empty($matches[1])){
			$matches = array_unique($matches[1]);//过滤重复值
			foreach ($matches as $key){
				if($key=='assign'){
					$content = preg_replace("/\{$key(.+?)\}/sm",'<?php echo $this->assign\\1; ?>',$content);
				}elseif(function_exists($key) || $key=='echo' || $key=='print' ||strpos($key, '::')!==false){
					$echo = in_array($key,array('print','print_r','printf','echo','var_dump','var_export')) ? '' : 'echo ';
					$content = preg_replace("/\{$key(.+?)\}/sm",'<?php '.$echo.$key.'\\1; ?>',$content);
					$content = preg_replace("/\{@$key(.+?)\}/sm",'<?php '.$key.'\\1; ?>',$content);
				}
			}
		}
		unset($matches);
		$content = str_replace(array('{php}','{/php}'),array('<?php ',' ?>'),$content);
		if ($compile !== false){
			Helper::write($compile, $content);
			unset($content);
		}else{
			return $content;
		}
	}
}

?>